/*
* Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { X3DModal } = require("./webgl1/X3DFast");
const { X2DBeta, X3DFast } = require("./X2DInterface");
const { XMat4 } = require("./XMat4");
const { XTexture } = require("./XTexture");
const { Scr } = require("../XDefine");
const { XTools } = require("../XTools");
const { LogManager } = require("../../src/ros1/LogManager");

// getElementsByTagNameNS
// getNamedItem
// getElementById
// getElementsByTagName
// getElementsByName
// getElementsByClassName
// getAttribute
// hasAttribute

// setAttribute
// removeAttribute
// getAttributeNode
// setAttributeNode
// removeAttributeNode

class DAEObject {
    constructor(options) {
        this.loaded = false;
        this.tmat = new XMat4();
        this.resLoader = null;
        if (options.resLoader) {
            this.resLoader = options.resLoader;
        }
        if (options.url) {
            this.loadFromUrl(options.url);
        } else if (options.xmlString) {
            try {
                this.loadFromXmlString(options.xmlString);
            } catch (e) {
                LogManager.E("载入dae文件失败:", options.name);
                console.error(e.message);
            }
        }
    }
    loadFromUrl(url) {
        fetch(url)
            .then(response => response.text())
            .then(data => {
                this.loadFromXmlString(data);
            })
            .catch(error => console.error('Error:', error));
    }
    loadFromXmlString(xmlString) {
        let parser = new DOMParser();
        let xmlData = parser.parseFromString(xmlString, "text/xml");
        this.collada = xmlData.getElementsByTagName("COLLADA")[0]
        this.loadGeometry();
        this.loadScene();
        this.loaded = true;
    }
    loadScene() {
        let visual_scene = this.collada.getElementsByTagName("visual_scene")[0];
        let nodes = visual_scene.getElementsByTagName("node");
        this.scene = {
            nodes: {},
            bounds: {
                min: [999, 999, 999],
                max: [-999, -999, -999],
            },
        }
        for (let node of nodes) {
            let name = node.getAttribute("id");
            let m = node.getElementsByTagName("matrix")[0].textContent.split(" ").map(Number);
            let transform = new XMat4();
            transform.mat = [
                [m[0], m[4], m[8], m[12]],
                [m[1], m[5], m[9], m[13]],
                [m[2], m[6], m[10], m[14]],
                [m[3], m[7], m[11], m[15]],
            ];

            let instance_geometry = node.getElementsByTagName("instance_geometry")[0];
            let url = instance_geometry.getAttribute("url").slice(1);
            let g = this.geometry[url];
            this.scene.nodes[name] = {
                matrix: transform,
                geometry: g,
            }
            this.scene.bounds.min[0] = Math.min(g.bounds.min[0], this.scene.bounds.min[0]);
            this.scene.bounds.min[1] = Math.min(g.bounds.min[1], this.scene.bounds.min[1]);
            this.scene.bounds.min[2] = Math.min(g.bounds.min[2], this.scene.bounds.min[2]);
            this.scene.bounds.max[0] = Math.max(g.bounds.max[0], this.scene.bounds.max[0]);
            this.scene.bounds.max[1] = Math.max(g.bounds.max[1], this.scene.bounds.max[1]);
            this.scene.bounds.max[2] = Math.max(g.bounds.max[2], this.scene.bounds.max[2]);
        }
    }
    bounds() {
        return this.scene.bounds;
    }
    loadGeometry() {
        this.geometry = {};
        let gs = this.collada.getElementsByTagName("geometry");
        for (let g of gs) {
            let gid = g.getAttribute("id");
            this.geometry[gid] = {
                sources: {},
                vertices: null,
                triangles: {
                    data: null,
                    count: 0,
                    stride: 0,
                    material: null,
                },
                modal: null,
                bounds: {
                    min: [999, 999, 999],
                    max: [-999, -999, -999],
                },
            }
            let mesh = g.getElementsByTagName("mesh")[0];
            let sources = mesh.getElementsByTagName("source");
            for (let source of sources) {
                let sid = source.getAttribute("id");
                let float_array = source.getElementsByTagName("float_array")[0];
                let accessor = source.getElementsByTagName("technique_common")[0].getElementsByTagName("accessor")[0];

                this.geometry[gid].sources[sid] = {
                    data: float_array.textContent.split(" ").map(Number),
                    count: parseInt(accessor.getAttribute("count")),
                    stride: parseInt(accessor.getAttribute("stride")),
                }
            }
            let vertices = mesh.getElementsByTagName("vertices")[0];
            let input = vertices.getElementsByTagName("input")[0];
            this.geometry[gid].vertices = this.geometry[gid].sources[input.getAttribute("source").slice(1)];

            let triangles = mesh.getElementsByTagName("triangles")[0];
            let inputs = triangles.getElementsByTagName("input");
            let p = triangles.getElementsByTagName("p")[0];
            let material = triangles.getAttribute("material");
            this.geometry[gid].triangles.data = p.textContent.split(" ").map(Number);
            this.geometry[gid].triangles.count = parseInt(triangles.getAttribute("count"));
            this.geometry[gid].triangles.stride = inputs.length;
            if (material) {
                let materials = this.collada.getElementsByTagName("material");
                for (let m of materials) {
                    if (m.getAttribute("id") === material) {
                        let instance_effect = m.getElementsByTagName("instance_effect")[0];
                        let effect_url = instance_effect.getAttribute("url").slice(1);
                        let effects = this.collada.getElementsByTagName("effect");
                        for (let e of effects) {
                            if (e.getAttribute("id") === effect_url) {
                                let init_from = e.getElementsByTagName("init_from")[0].textContent;
                                let images = this.collada.getElementsByTagName("image");
                                for (let img of images) {
                                    if (img.getAttribute("id") === init_from) {
                                        let img_name = img.getElementsByTagName("init_from")[0].textContent;
                                        if (this.resLoader) {
                                            this.resLoader(img_name, (rid) => {
                                                this.geometry[gid].triangles.material = rid;
                                                if (this.loaded && this.geometry[gid].modal) {
                                                    this.geometry[gid].modal.rid = rid;
                                                }
                                            });
                                        } else {
                                            this.geometry[gid].triangles.material = XTexture.LoadTextureFromImage("dae/" + img_name);
                                        }
                                        break;
                                    }
                                }
                                break;
                            }
                        }
                        break;
                    }
                }
            }

            let vs, ts, ns;
            for (let i of inputs) {
                let semantic = i.getAttribute("semantic");
                let source = i.getAttribute("source").slice(1);
                let offset = parseInt(i.getAttribute("offset"));
                if (semantic === "VERTEX") {
                    vs = this.geometry[gid].vertices.data;
                } else if (semantic === "NORMAL") {
                    ns = this.geometry[gid].sources[source].data;
                } else if (semantic === "TEXCOORD") {
                    ts = this.geometry[gid].sources[source].data;
                }
            }
            let fs = this.geometry[gid].triangles.data;
            let buf = [];
            let newfs = [];
            let stride = ts ? 3 : 2;
            for (let i = 0; i < fs.length; i += stride) {
                let pv = fs[i];
                let pn = fs[i + 1];

                buf.push(vs[pv * 3], vs[pv * 3 + 1], vs[pv * 3 + 2]);
                buf.push(ns[pn * 3], ns[pn * 3 + 1], ns[pn * 3 + 2]);
                if (ts) {
                    let pt = fs[i + 2];
                    buf.push(ts[pt * 2], 1 - ts[pt * 2 + 1]);
                } else {
                    buf.push(0, 0);
                }
                newfs.push(i / stride);
                this.geometry[gid].bounds.min[0] = Math.min(this.geometry[gid].bounds.min[0], vs[pv * 3]);
                this.geometry[gid].bounds.min[1] = Math.min(this.geometry[gid].bounds.min[1], vs[pv * 3 + 1]);
                this.geometry[gid].bounds.min[2] = Math.min(this.geometry[gid].bounds.min[2], vs[pv * 3 + 2]);
                this.geometry[gid].bounds.max[0] = Math.max(this.geometry[gid].bounds.max[0], vs[pv * 3]);
                this.geometry[gid].bounds.max[1] = Math.max(this.geometry[gid].bounds.max[1], vs[pv * 3 + 1]);
                this.geometry[gid].bounds.max[2] = Math.max(this.geometry[gid].bounds.max[2], vs[pv * 3 + 2]);
            }
            // buf = [
            //     0, 0, 0, 0, 0, 0, 0, 0,
            //     100, 0, 0, 0, 0, 0, 0, 0,
            //     0, 100, 0, 0, 0, 0, 0, 0,
            //     0, 0, 100, 0, 0, 0, 0, 0,
            //     100, 100, 0, 0, 0, 0, 0, 0,
            //     0, 100, 100, 0, 0, 0, 0, 0,
            //     100, 100, 100, 0, 0, 0, 0, 0,
            // ];
            // newfs = [0, 1, 2, 0, 1, 3, 0, 1, 4, 0, 1, 5, 0, 1, 6];
            this.geometry[gid].w = this.geometry[gid].bounds.max[0] - this.geometry[gid].bounds.min[0];
            this.geometry[gid].h = this.geometry[gid].bounds.max[1] - this.geometry[gid].bounds.min[1];
            this.geometry[gid].d = this.geometry[gid].bounds.max[2] - this.geometry[gid].bounds.min[2];
            this.geometry[gid].modal = new X3DModal(buf, newfs, this.geometry[gid].triangles.material);
        }
        console.log("loadGeometry");
    }
    render(options) {
        if (!this.loaded) {
            return;
        }
        for (let node in this.scene.nodes) {
            let n = this.scene.nodes[node];
            this.tmat.Copy(n.matrix);
            this.tmat.Mult(options.mat);

            // tmat.Rotate(0, 0, XTools.TIME_MS / 10);
            // // tmat.Move(- this.scene.bounds.max[0], 0, -this.scene.bounds.max[2]);
            // tmat.Scale(100, 100, 100);
            n.geometry.modal.render({ mat: this.tmat, color: options.color, pickPoint: options.pickPoint });
            if (options.withBounds) X3DFast.drawFrame(n.geometry.bounds.min, n.geometry.bounds.max, this.tmat, 0xff00ff00);
            // X3DFast.fillFrame(n.geometry.bounds.min, n.geometry.bounds.max, tmat, 0x8000ff00);
        }
    }
    pick(options) {//拾取物体
        if (!this.loaded) {
            return;
        }
        for (let node in this.scene.nodes) {
            let n = this.scene.nodes[node];
            this.tmat.Copy(n.matrix);
            this.tmat.Mult(options.mat);
            this.tmat.Mult(X3DFast.umat);

            if (n.geometry.modal.pick({ mat: this.tmat, v0: options.v0, v1: options.v1 })) {
                return true;
            }
        }
        return false;
    }
    render_() {
        if (!this.loaded) {
            return;
        }
        // X2DBeta.drawLine(0, Scr.logich / 2, Scr.logicw, Scr.logich / 2, 0xffff0000, 1);
        // X2DBeta.drawLine(Scr.logicw / 2, 0, Scr.logicw / 2, Scr.logich, 0xffff0000, 1);
        X3DFast.swapMode3D();
        {
            let tmat = new XMat4();

            for (let node in this.scene.nodes) {
                let n = this.scene.nodes[node];
                tmat.Copy(n.matrix);

                tmat.Rotate(0, 0, XTools.TIME_MS / 10);
                // tmat.Move(- this.scene.bounds.max[0], 0, -this.scene.bounds.max[2]);
                tmat.Scale(100, 100, 100);
                n.geometry.modal.render({ mat: tmat, color: 0xffffffff });
                X3DFast.drawFrame(n.geometry.bounds.min, n.geometry.bounds.max, tmat, 0xffffffff);
                // X3DFast.fillFrame(n.geometry.bounds.min, n.geometry.bounds.max, tmat, 0x8000ff00);
            }

            // for (let gn in this.geometry) {
            //     tmat.Unit();
            //     tmat.Rotate(0, 0, XTools.TIME_MS / 10);
            //     tmat.Move(- this.geometry[gn].rect.maxx, 0, -this.geometry[gn].rect.maxz);
            //     tmat.Scale(50, 50, 50);
            //     // tmat.Move(Scr.logicw / 2, 0, Scr.logich / 2);
            //     this.geometry[gn].modal.render(tmat, 0x80ffffff);
            //     let b = this.geometry[gn].bounds;
            //     X3DFast.drawFrame(b.min, b.max, tmat, 0xff00ff00);
            // }
        }
        X2DBeta.swapMode2D();
    }
}
// class TestDAE {
//     constructor() {
//         console.log("TestDAE constructor");
//         this.dae = new DAEObject('dae/a.dae');
//     }
//     static ggg() {
//         g_td.dae.render();
//     }
// }

// var g_td = new TestDAE();

module.exports = {
    DAEObject
};